import router, { resetRouter } from '@router'
import remoteRoutes from '@router/modules/remote'

function hasRouteChildren (item = {}, keyName = 'children') {
  return item[keyName] && Array.isArray(item[keyName]) && item[keyName].length > 0
}

function importComponent (file) {
  file = file === '/index' ? file : file.replace(/\/[^/]+$/g, _ => _ === '/index' ? '' : _)
  if (remoteRoutes[file]) {
    return remoteRoutes[file]
  } else {
    return null
  }
}

function getMenus (menuSource, flatten) {
  /**
   * @description 转换驼峰 menuAdd 类 字母为 menu-add
   */
  function replaceToLowerCase (letter) {
    if (!letter) { return letter }
    return letter.replace(/([A-Z])/g, function (match) {
      return '-' + match.toLowerCase()
    })
  }

  /**
   * @description 依次处理原始数据，返回处理后的菜单
   * @param {Array} menus 上次处理返回的结果
   * @param {Object} sourceItem 原始数据的一项
   */
  function maker (menus, sourceItem, parent) {
    // 扁平化树形菜单，下标为菜单的id
    flatten[sourceItem.id] = sourceItem
    // 初始化更正关键值，方便理解
    if (sourceItem.path) {
      sourceItem.uri = sourceItem.path
    }
    sourceItem.menuName = sourceItem.title
    sourceItem.routeName = sourceItem.uri
    sourceItem.routePath = sourceItem.uri

    sourceItem.routeComponent = replaceToLowerCase((sourceItem.uri || '').split('?')[0])

    sourceItem.routeComponent = sourceItem.routeComponent.replace(/\/:.+/gi, function (a) {
      let props
      sourceItem.routePath = sourceItem.routePath.replace(/:[^/]+/gi, function (param) {
        param = param.split('=')
        if (param[1]) {
          if (!props) { props = {} }
          props[param[0].split(':')[1]] = param[1]
        }
        return param[0]
      })
      if (props) {
        sourceItem.props = props
      }
      return ''
    })

    let menu = {}
    menu.title = sourceItem.menuName
    menu.icon = sourceItem.icon
    menu.path = sourceItem.routePath
    menu.id = sourceItem.id
    menu.parentId = sourceItem.parentId
    if (sourceItem.props) {
      menu.path = menu.path.replace(/:([^/:]+)/gi, function (_, param) {
        return sourceItem.props[param]
      })
      menu.props = sourceItem.props
    }
    if (hasRouteChildren(sourceItem)) {
      menu.children = sourceItem.children.reduce(maker, [])
    }
    menus.push(menu)
    return menus
  }
  return menuSource.reduce(maker, [])
}

/**
 * @description 从接口返回的数据中计算出路由
 * @param {Array} menuSource 接口返回的原始菜单数据
 */
function getRoutes (menuSource) {
  /**
   * @description 检验是否为合法路由
   * @param {Object} sourceItem 原始数据的一项
   */
  function isEffectiveRoute (sourceItem) {
    // if (sourceItem.menuType !== +process.env.VUE_APP_DICT_MENU_TYPE_MENU) return
    const hasAllRequiredProperties = [
      'menuName',
      'routeName',
      'routePath',
      'routeComponent'
    ].reduce((res, keyname) => !((!res || !sourceItem[keyname] === '')), true)
    if (!hasAllRequiredProperties) return
    return true
  }
  /**
   * @description 检验是否已经注册过此路由
   * @description 在 vue-router 中路由的 name 不允许重复
   * @param {Array} registered 已经注册的路由
   * @param {Object} sourceItem 原始数据的一项
   */
  function isUnregistered (registered, sourceItem) {
    return !registered.find(item => item.title === sourceItem.routeName)
  }
  /**
   * @description 依次处理原始数据，返回处理后的路由
   * @param {Array} routes 上次处理返回的结果
   * @param {Object} sourceItem 原始数据的一项
   */
  function maker (routes, sourceItem) {
    if (sourceItem.routePath && !hasRouteChildren(sourceItem) && isEffectiveRoute(sourceItem) && isUnregistered(routes, sourceItem)) {
      // 没有子菜单 并且这个路由没有被加入到动态路由列表 处理当前路由
      const component = importComponent(sourceItem.routeComponent)
      if (component) {
        const meta = component ? (component.meta || {}) : {}
        const route = {
          path: sourceItem.routePath,
          name: sourceItem.routeName,
          ...component,
          meta: {
            ...meta,
            menuSource: sourceItem,
            title: sourceItem.menuName,
            auth: true,
            id: sourceItem.id,
            parentId: sourceItem.parentId
          },
          componentName: meta.componentName
        }

        routes.push(route)
      }
    }
    if (hasRouteChildren(sourceItem)) {
      // 有子菜单 递归获取所有子菜单的路由
      const childRoutes = sourceItem.children.reduce(maker, [])
      routes = routes.concat(childRoutes)
    }
    return routes
  }
  return menuSource.reduce(maker, [])
}

export default context => ({
  namespaced: true,
  state: {
    // 顶栏菜单
    header: [],
    // 侧栏菜单
    aside: [],
    // 其他菜单
    other: [],
    // 扁平化菜单数据
    flatten: {}
  },
  actions: {
    async load ({ state, commit, dispatch }, { to, data } = {}) {
      if (state.header && state.header.length) { return }
      let source
      if (data) {
        source = data
      } else {
        source = await context.api.TREE_MENU()
        source = source.data
        if (!source) {
          source = []
        }
      }
      // [ 菜单 ] 计算菜单
      const flatten = {}
      const menus = getMenus(source, flatten)
      // [ 菜单 ] 设置顶栏菜单
      commit('ec/menu/headerSet', menus, { root: true })
      // 菜单以id扁平化设置
      commit('ec/menu/flattenSet', flatten, { root: true })
      // [ 路由 ] 计算路由
      const routes = getRoutes(source)
      // [ 路由 ] 重新设置路由
      resetRouter(routes)
      if (to) {
        await router.replace(to)
      }
    }
  },
  mutations: {
    /**
     * @description 设置顶栏菜单
     * @param {Object} state state
     * @param {Array} menu menu setting
     */
    headerSet (state, menu) {
      // store 赋值
      state.header = menu
    },
    /**
     * @description 设置侧边栏菜单
     * @param {Object} state state
     * @param {Array} menu menu setting
     */
    asideSet (state, menu) {
      // store 赋值
      state.aside = menu
    },
    /**
     * @description 设置侧边栏菜单
     * @param {Object} state state
     * @param {Array} menu menu setting
     */
    otherSet (state, menu) {
      // store 赋值
      state.other = menu
    },
    /**
     * @description 设置扁平化菜单数据
     * @param {Object} state state
     * @param {Array} menu menu
     */
    flattenSet (state, menu) {
      // store 赋值
      state.flatten = menu
    }
  }
})
